"""
Written by Bram Cohen
modified for multitracker by John Hoffman
see LICENSE.txt for license information
"""

import sys
import wx
import os

import wx.gizmos as gizmos
import wx.lib.agw.customtreectrl as CustomTreeCtrl

from os.path import join, isdir, exists, normpath, split, getsize
from threading import Event, Thread
from shutil import copy2

from traceback import print_exc

from btmakemetafile import make_meta_file, completedir, subfiles, calcsize

from LMG.GUI.Base.guiupdate import SafeInvocation
from LMG.Utility.helpers import union, comma_format, get_metainfo
from LMG.Utility.constants import *


ML_TextCtrlStyle = wx.TE_MULTILINE
if wx.Platform != "__WXMAC__": ML_TextCtrlStyle |= wx.TE_DONTWRAP

################################################################

ignorelist = ['thumbs.db', 'Thumbs.db', 'sthumbs.db', '.ds_store']

def shouldIgnore(filename):
    """
    Returns True if a file should be ignored by default
    (at this point, ignore torrent files and thumbs.db)
    """
    if filename[-8:] == ".torrent":
        return True

    try:
        # try comparing the lowercase version of the filename
        filename = filename.lower()
    except:
        # might get an exception for unicode files
        pass
    if filename in ignorelist:
        return True    
    
################################################################

class MiscInfoPanel(wx.Panel):
    """
    Panel for defining miscellaneous settings for a torrent
    """
    def __init__(self, parent, dialog):
        wx.Panel.__init__(self, parent, -1)
        
        self.dialog = dialog

        outerbox = wx.BoxSizer(wx.VERTICAL)

        # Created by:
        outerbox.Add(wx.StaticText(self, -1, _('Created by:')), 0, wx.EXPAND|wx.ALL, 5)
        self.createdBy = wx.TextCtrl(self, -1)
        outerbox.Add(self.createdBy, 0, wx.EXPAND|wx.ALL, 5)

        # Comment:        
        outerbox.Add(wx.StaticText(self, -1, _('Comment:')), 0, wx.EXPAND|wx.ALL, 5)
        self.commentCtl = wx.TextCtrl(self, -1, style = ML_TextCtrlStyle)
        if wx.Platform == "__WXMAC__":
            self.commentCtl.MacCheckSpelling(True)
        outerbox.Add(self.commentCtl, 1, wx.EXPAND|wx.ALL, 5)
      
        self.SetSizerAndFit(outerbox)
        
        self.loadValues()

    def loadValues(self, Read = None):
        if Read is None:
            Read = utility.makerconfig.Read
        
        self.createdBy.SetValue(Read('created_by'))
        self.commentCtl.SetValue(Read('comment'))

    def saveConfig(self, event = None):
        utility.makerconfig.Write('created_by', self.createdBy.GetValue())
        utility.makerconfig.Write('comment', self.commentCtl.GetValue())
        
    def getParams(self):
        params = {}
        
        comment = self.commentCtl.GetValue()
        if comment != '':
            params['comment'] = comment

        createdby = self.createdBy.GetValue()
        if createdby != '':
            params['created by'] = createdby
            
        return params


################################################################

class TrackerInfoPanel(wx.Panel):
    """
    Panel for defining tracker settings for a torrent
    """
    def __init__(self, parent, dialog):
        wx.Panel.__init__(self, parent, -1)
        
        self.dialog = dialog

        outerbox = wx.BoxSizer(wx.VERTICAL)

        self.announcehistory = []

        # Announce url:
        outerbox.Add(wx.StaticText(self, -1, _('Announce url')), 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)

        announceurl_box = wx.BoxSizer(wx.HORIZONTAL)
       
        self.annCtl = wx.ComboBox(self, -1, "", choices = self.announcehistory, style=wx.CB_DROPDOWN)
        announceurl_box.Add(self.annCtl, 1, wx.ALIGN_CENTER_VERTICAL|wx.RIGHT, 3)
        
        button = wx.Button(self, -1, "+", size = (30, -1))
        button.SetToolTipString(_('Add'))
        wx.EVT_BUTTON(self, button.GetId(), self._addAnnounce)
        announceurl_box.Add(button, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 1)

        button2 = wx.Button(self, -1, "-", size = (30, -1))
        button2.SetToolTipString(_('Remove'))
        wx.EVT_BUTTON(self, button2.GetId(), self._removeAnnounce)
        announceurl_box.Add(button2, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 1)

        abutton = wx.Button(self, -1, "...", size = (30, -1))
        abutton.SetToolTipString(_('Load announce list from torrent'))
        wx.EVT_BUTTON(self, abutton.GetId(), self._announceCopy)
        announceurl_box.Add(abutton, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 1)

        outerbox.Add(announceurl_box, 0, wx.EXPAND)

        # Announce List:        
        outerbox.Add(wx.StaticText(self, -1, _('Announce list')), 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
       
        self.annListCtl = wx.TextCtrl(self, -1, size = (-1, 75), style = ML_TextCtrlStyle)
        self.annListCtl.SetToolTipString(_('A list of announces separated by commas or whitespace. Put each tier in a new line'))
        outerbox.Add(self.annListCtl, 1, wx.EXPAND|wx.ALL, 5)

        # DHT Nodes        
        outerbox.Add(wx.StaticText(self, -1, _('DHT Nodes')), 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
       
        self.dhtNodesCtl = wx.TextCtrl(self, -1, size = (-1, 75), style = ML_TextCtrlStyle)
        self.dhtNodesCtl.SetToolTipString(_('A list of DHT nodes (host:port) separated by commas or whitespace'))
        outerbox.Add(self.dhtNodesCtl, 0, wx.EXPAND|wx.ALL, 5)
        
        # HTTP Seeds:
        outerbox.Add(wx.StaticText(self, -1, _('HTTP seeds')), 0, wx.EXPAND|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
       
        self.httpSeeds = wx.TextCtrl(self, -1)
        self.httpSeeds.SetToolTipString(_('A list of HTTP seeds separated by commas or whitespace.'))
        outerbox.Add(self.httpSeeds, 0, wx.EXPAND|wx.ALL, 5)

        self.privatetorrent = wx.CheckBox(self, -1, _('Private torrent'))
        outerbox.Add(self.privatetorrent, 0, wx.ALIGN_LEFT|wx.ALL, 5)
        self.privatetorrent.SetToolTipString(_('When checked, clients that support DHT and/or Peer Exchange will not use them to get peer information'))
      
        self.SetSizerAndFit(outerbox)
        
        self.loadValues()

    def loadValues(self, Read = None):
        if Read is None:
            Read = utility.makerconfig.Read
        
        self.annCtl.Clear()
        self.annCtl.SetValue(Read('announcedefault'))

        self.announcehistory = Read('announcehistory', "bencode-list")
        for announceurl in self.announcehistory:
            self.annCtl.Append(announceurl)

        self.annListCtl.SetValue(Read('announce-list'))
        self.dhtNodesCtl.SetValue(Read('dhtnodes'))
        self.httpSeeds.SetValue(Read('httpseeds'))
        
        self.privatetorrent.SetValue(Read('privatetorrent', "boolean"))

    def saveConfig(self, event = None):
        index = self.annCtl.GetSelection()
        if index != -1:
            utility.makerconfig.Write('announcedefault', self.annCtl.GetValue())
        utility.makerconfig.Write('announcehistory', self.announcehistory, "bencode-list")
        utility.makerconfig.Write('announce-list', self.annListCtl.GetValue())
        utility.makerconfig.Write('dhtnodes', self.dhtNodesCtl.GetValue())
        utility.makerconfig.Write('httpseeds', self.httpSeeds.GetValue())
        
        utility.makerconfig.Write('privatetorrent', self.privatetorrent.GetValue(), "boolean")

    def _addAnnounce(self, event = None):
        announceurl = self.annCtl.GetValue()

        # Don't add to the list if it's already present or the string is empty
        announceurl = announceurl.strip()
        if not announceurl or announceurl in self.announcehistory:
            return
       
        self.announcehistory.append(announceurl)
        self.annCtl.Append(announceurl)
        
    def _removeAnnounce(self, event = None):
        index = self.annCtl.GetSelection()
        if index != -1:
            announceurl = self.annCtl.GetValue()
            self.annCtl.Delete(index)
            try:
                self.announcehistory.remove(announceurl)
            except:
                pass

    def _announceCopy(self, event = None):
        dl = wx.FileDialog(self.dialog, 
                           _('Choose .torrent file to use'), 
                           '', 
                           '', 
                            _('Torrent Files') + ' (*.torrent)|*.torrent', 
                           wx.OPEN)
        if dl.ShowModal() == wx.ID_OK:
            try:
                metainfo = get_metainfo(dl.GetPath())
                if (metainfo is None):
                    return
                self.annCtl.SetValue(metainfo['announce'])
                if 'announce-list' in metainfo:
                    list = []
                    for tier in metainfo['announce-list']:
                        for tracker in tier:
                            list += [tracker, ', ']
                        del list[-1]
                        list += ['\n']
                    liststring = ''
                    for i in list:
                        liststring += i
                    self.annListCtl.SetValue(liststring+'\n\n')
                else:
                    self.annListCtl.SetValue('')
            except:
                return                

    def _getAnnounceList(self):
        text = self.annListCtl.GetValue()
        list = []
        for tier in text.split('\n'):
            sublist = []
            tier = tier.replace(',', ' ')
            for tracker in tier.split(' '):
                if tracker != '':
                    sublist += [tracker]
            if sublist:
                list.append(sublist)
        return list
        
    def _getHTTPSeedList(self):
        text = self.httpSeeds.GetValue()
        list = []
        for tier in text.split('\n'):
            tier = tier.replace(',', ' ')
            for tracker in tier.split(' '):
                if tracker != '':
                    list.append(tracker)
        return list
    
    def _getNodesList(self):
        text = self.dhtNodesCtl.GetValue()
        text = text.replace(',', ' ')
        text = text.replace('\n', ' ')
        
        list = []
        for node in text.split(' '):
            if node.strip() == "":
                continue            
            values = node.split(':')
            if len(values) != 2:
                continue
            host, port = values
            try:
                port = int(port)
                assert port > 0 and port <= 65535
                assert len(host) > 0 and host != "127.0.0.1" and host != "localhost"
            except:
                continue
            list.append([host.encode("utf-8"), port])
        return list

    def getParams(self):
        params = {}
        
        # Announce list
        annlist = self._getAnnounceList()
        if annlist:
            params['real_announce_list'] = annlist
        
        # Announce URL
        announceurl = None
        index = self.annCtl.GetSelection()
        if annlist and index == -1:
            # If we don't have an announce url specified,
            # try using the first value in announce-list
            tier1 = annlist[0]
            if tier1:
                announceurl = tier1[0]
        else:
            announceurl = self.annCtl.GetValue()
                
        if announceurl is None:
            # What should we do here?
            announceurl = ""
            
        params['announce'] = announceurl

        # DHT Nodes
        nodes = self._getNodesList()
        if nodes:
            params['nodes'] = nodes
        
        # HTTP Seeds
        httpseedlist = self._getHTTPSeedList()
        if httpseedlist:
            params['real_httpseeds'] = httpseedlist
        
        # Privatetorrent
        if self.privatetorrent.GetValue():
            params['private'] = 1
        
        return params


################################################################

class FileInfoPanel(wx.Panel):
    """
    Class for choosing a file when creating a torrent
    """
    def __init__(self, parent, dialog):
        wx.Panel.__init__(self, parent, -1)
        
        self.dialog = dialog

        outerbox = wx.BoxSizer(wx.VERTICAL)

        # Piece size:
        piecesize_box = wx.BoxSizer(wx.HORIZONTAL)
        
        piecesize_box.Add(wx.StaticText(self, -1, _('Piece size :')), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
        
        abbrev_mb = " " + _('MB')
        abbrev_kb = " " + _('KB')
        
        piece_choices = [_('automatic'), 
                         '2' + abbrev_mb, 
                         '1' + abbrev_mb, 
                         '512' + abbrev_kb, 
                         '256' + abbrev_kb, 
                         '128' + abbrev_kb, 
                         '64' + abbrev_kb, 
                         '32' + abbrev_kb]
        self.piece_length = wx.Choice(self, -1, choices = piece_choices)
        self.piece_length_list = [0, 21, 20, 19, 18, 17, 16, 15]
        piecesize_box.Add(self.piece_length, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5)
        
        outerbox.Add(piecesize_box, 0, wx.EXPAND)

        # Save torrent :
        savetorrentbox = wx.StaticBoxSizer(wx.StaticBox(self, -1, _('Torrent location')), wx.VERTICAL)

        self.savetorrb1 = wx.RadioButton(self, -1, _('Save to default folder :'), (-1, -1), (-1, -1), wx.RB_GROUP)
        savetorrb2 = wx.RadioButton(self, -1, _('Save to folder containing source'), (-1, -1), (-1, -1))
        savetorrb3 = wx.RadioButton(self, -1, _('Ask where to save to'), (-1, -1), (-1, -1))
        self.savetor = [self.savetorrb1, savetorrb2, savetorrb3]

        savetordefbox = wx.BoxSizer(wx.HORIZONTAL)
        savetordefbox.Add(self.savetorrb1, 0, wx.ALIGN_CENTER_VERTICAL)
        self.savetordeftext = wx.TextCtrl(self, -1, "")
        browsebtn = wx.Button(self, -1, "...", style = wx.BU_EXACTFIT)
        browsebtn.Bind(wx.EVT_BUTTON, self._onBrowseDir)
        savetordefbox.Add(self.savetordeftext, 1, wx.ALIGN_CENTER_VERTICAL | wx.LEFT, 5)
        savetordefbox.Add(browsebtn, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 3)
        savetorrentbox.Add(savetordefbox, 0, wx.EXPAND)
        
        savetorrentbox.Add(savetorrb2, 0)

        savetorrentbox.Add(savetorrb3, 0, wx.TOP, 4)

        outerbox.Add(savetorrentbox, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)

        optionalhash_title = wx.StaticBox(self, -1, _('Optional hashes:'))
        optionalhash = wx.StaticBoxSizer(optionalhash_title, wx.VERTICAL)

        self.makehash_md5 = wx.CheckBox(self, -1, _('MD5'))
        optionalhash.Add(self.makehash_md5, 0)

        self.makehash_crc32 = wx.CheckBox(self, -1, _('CRC-32'))
        optionalhash.Add(self.makehash_crc32, 0, wx.TOP, 4)

        self.makehash_sha1 = wx.CheckBox(self, -1, _('SHA-1'))
        optionalhash.Add(self.makehash_sha1, 0, wx.TOP, 4)
        
        outerbox.Add(optionalhash, 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)

        self.startnow = wx.CheckBox(self, -1, _('Start seeding immediately'))
        outerbox.Add(self.startnow, 0, wx.ALIGN_LEFT|wx.ALL, 5)

        self.SetSizerAndFit(outerbox)
        
        self.loadValues()

#        panel.DragAcceptFiles(True)
#        wx.EVT_DROP_FILES(panel, self.selectdrop)

    def loadValues(self, Read = None):
        if Read is None:
            Read = utility.makerconfig.Read
        self.startnow.SetValue(Read('startnow', "boolean"))
        self.makehash_md5.SetValue(Read('makehash_md5', "boolean"))
        self.makehash_crc32.SetValue(Read('makehash_crc32', "boolean"))
        self.makehash_sha1.SetValue(Read('makehash_sha1', "boolean"))
        
        self.savetor[Read('savetorrent', "int")].SetValue(True)        
        self.piece_length.SetSelection(Read('piece_size', "int"))
        self.savetordeftext.SetValue(Read('savetordeffolder'))
        

    def saveConfig(self, event = None):        
        utility.makerconfig.Write('startnow', self.startnow.GetValue(), "boolean")
        
        utility.makerconfig.Write('makehash_md5', self.makehash_md5.GetValue(), "boolean")
        utility.makerconfig.Write('makehash_crc32', self.makehash_crc32.GetValue(), "boolean")
        utility.makerconfig.Write('makehash_sha1', self.makehash_sha1.GetValue(), "boolean")
            
        utility.makerconfig.Write('savetordeffolder', self.savetordeftext.GetValue())

        for i in range(3):
            if self.savetor[i].GetValue():
                utility.makerconfig.Write('savetorrent', i)
                break
        utility.makerconfig.Write('piece_size', self.piece_length.GetSelection())

    def _onBrowseDir(self, event = None):
        dlg = wx.DirDialog(self.dialog, 
                           _('Choose a default folder to save torrents'), 
                           style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
        if dlg.ShowModal() == wx.ID_OK:
            self.savetordeftext.SetValue(dlg.GetPath())
        dlg.Destroy()

#    def selectdrop(self, x):
#        list = x.m_files
#        self.dirCtl.SetLabel(x[0])
    
    def getParams(self):
        params = {}
        self.targeted = []
        
        params['piece_size_pow2'] = self.piece_length_list[self.piece_length.GetSelection()]
        
        gethash = {}
        if self.makehash_md5.GetValue():
            gethash['md5'] = True
        if self.makehash_crc32.GetValue():
            gethash['crc32'] = True
        if self.makehash_sha1.GetValue():
            gethash['sha1'] = True   
        params['gethash'] = gethash

        for i in range(3):
            if self.savetor[i].GetValue():
                break
        
        if i == 0:
            defdestfolder = self.savetordeftext.GetValue()                    

            # Check if default download folder is not a file and create it if necessary
            if exists(defdestfolder):
                if not isdir(defdestfolder):
                    dlg = wx.MessageDialog(self, 
                                           message = _('The default download directory is a file') + '\n' + \
                                                     _('Torrent will be saved to folder containing source'), 
                                           caption = _('Error'), 
                                           style = wx.OK | wx.ICON_ERROR)
                    dlg.ShowModal()
                    dlg.Destroy()
                    defdestfolder = ""
            else:
                try:
                    os.makedirs(defdestfolder)
                except:
                    dlg = wx.MessageDialog(self, 
                                           message = _('This name cannot be used as a Windows file or folder name.') + '\n'+ \
                                                     _('Torrent will be saved to folder containing source'), 
                                           caption = _('Error'), 
                                           style = wx.OK | wx.ICON_ERROR)
                    dlg.ShowModal()
                    dlg.Destroy()
                    defdestfolder = ""
                     
            params['target'] = defdestfolder
                
            self.targeted = defdestfolder                 

        elif i == 2:
            dl = wx.DirDialog(self, style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
            result = dl.ShowModal()
            dl.Destroy()
            if result != wx.ID_OK:
                return
            params['target'] = dl.GetPath()
            self.targeted = dl.GetPath()
        else:
            self.targeted = ""

        return params
    
    def getTargeted(self):
        targeted = self.targeted
        return targeted


################################################################

class CheckListCtrl(CustomTreeCtrl.CustomTreeCtrl):
    """
    A ListCtrl with checkboxes
    """
    def __init__(self, parent):

        style = wx.SUNKEN_BORDER
        ctstyle = wx.TR_DEFAULT_STYLE|CustomTreeCtrl.TR_AUTO_CHECK_CHILD
        if wx.VERSION_STRING < "2.8.11.0":
            CustomTreeCtrl.CustomTreeCtrl.__init__(self, parent, -1, style = style, ctstyle = ctstyle)
        else:
            CustomTreeCtrl.CustomTreeCtrl.__init__(self, parent, -1, style = style, agwStyle = ctstyle)


################################################################

class FileSelectionPanel(wx.Panel):
    """
    Creates a panel for selecting files
    """
    def __init__(self, parent, dialog):
        wx.Panel.__init__(self, parent, -1)
        
        self.dialog = dialog
        
        self.isdir = False
        self.files = []
        self.filemap = {}
        self.basepath = ''

        outerbox = wx.BoxSizer(wx.VERTICAL)

        # Make torrent of:
        maketorrent_box = wx.BoxSizer(wx.HORIZONTAL)
        maketorrent_box.Add(wx.StaticText(self, -1, _('Source :')), 1, wx.ALIGN_CENTER_VERTICAL)

        button = wx.Button(self, -1, _('Dir'), style = wx.BU_EXACTFIT)
        wx.EVT_BUTTON(self, button.GetId(), self._selectDir)
        maketorrent_box.Add(button, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5)

        button2 = wx.Button(self, -1, _('File'), style = wx.BU_EXACTFIT)
        wx.EVT_BUTTON(self, button2.GetId(), self._selectFile)
        maketorrent_box.Add(button2, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT, 5)

        outerbox.Add(maketorrent_box, 0, wx.EXPAND)

        outerbox.Add(wx.StaticText(self, -1, _('Select files to include:')), 0, wx.EXPAND|wx.TOP|wx.BOTTOM, 5)

        self.filechecklist = CheckListCtrl(self)

        isz = (16,16)
        il = wx.ImageList(isz[0], isz[1])
        self.images = {}
        self.images['folder'] = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FOLDER,      wx.ART_OTHER, isz))
        self.images['folder_open'] = il.Add(wx.ArtProvider_GetBitmap(wx.ART_FILE_OPEN,   wx.ART_OTHER, isz))
        self.images['file'] = il.Add(wx.ArtProvider_GetBitmap(wx.ART_NORMAL_FILE, wx.ART_OTHER, isz))

        self.filechecklist.SetImageList(il)
        self.il = il
    
        self.filechecklist.Bind(CustomTreeCtrl.EVT_TREE_ITEM_CHECKED, self._onItemChecked)

        outerbox.Add(self.filechecklist, 1, wx.EXPAND)
        
        detailSizer = wx.FlexGridSizer(cols = 2, vgap = 6, hgap = 10)
        
        sizelabel_maxlen = max(len(_('File size')), len(_('Archive Size')))
        
        self.sizelabel = wx.StaticText(self, -1, '  ' * (sizelabel_maxlen + 5))
        detailSizer.Add(self.sizelabel) 

        self.sizetext = wx.StaticText(self, -1, '')
        detailSizer.Add(self.sizetext)
        
        detailSizer.Add(wx.StaticText(self, -1, _('Files') + ' : '))
        
        self.numfileslabel = wx.StaticText(self, -1, '0')
        detailSizer.Add(self.numfileslabel)
        
        outerbox.Add(detailSizer, 0, wx.EXPAND|wx.ALL, 5)

        self.SetSizerAndFit(outerbox)

#        panel.DragAcceptFiles(True)
#        wx.EVT_DROP_FILES(panel, self.selectdrop)

    def _getFileListing(self, basepath):        
        self.filechecklist.DeleteAllItems()
        
        self.files = []
        self.filemap = {}
        self.isdir = isdir(basepath)
        
        totalsize = 0L
                
        if self.isdir:
            root = self.filechecklist.AddRoot(basepath, ct_type = 1)
            self.filechecklist.SetPyData(root, None)
            
            self.filechecklist.SetItemImage(root, self.images['folder'], which = wx.TreeItemIcon_Normal)
            self.filechecklist.SetItemImage(root, self.images['folder_open'], which = wx.TreeItemIcon_Expanded)
            
            subs = subfiles(basepath)
            subs.sort()
            
            alreadypresent = {}
            
            for patharray, fullpath in subs:
                size = calcsize(fullpath)
                
                relativepath = u""
                firstpart = True
                addto = self.filechecklist.GetRootItem()
                # Add directories first
                if len(patharray) > 1:
                    for index in range(len(patharray) - 1):
                        part = patharray[index]
    
                        relativepath += "\\" + part
                        
                        if not relativepath in alreadypresent:
                            newitem = self.filechecklist.AppendItem(addto, part, ct_type = 1)
                            self.filechecklist.SetPyData(newitem, None)
                            
                            self.filechecklist.SetItemImage(newitem, self.images['folder'], which = wx.TreeItemIcon_Normal)
                            self.filechecklist.SetItemImage(newitem, self.images['folder_open'], which = wx.TreeItemIcon_Expanded)
                            alreadypresent[relativepath] = newitem
                        
                        addto = alreadypresent[relativepath]

                self.files.append((patharray, fullpath, patharray[-1], size, addto))
                
            # Add files after adding directories
            for index in range(len(self.files)):
                patharray, fullpath, filename, size, addto = self.files[index]
                # Only show size for individual items
                
                newitem = self.filechecklist.AppendItem(addto, filename, ct_type = 1)
                self.filemap[index] = newitem
                
                self.filechecklist.SetPyData(newitem, None)
                
                self.filechecklist.SetItemImage(newitem, self.images['file'], which = wx.TreeItemIcon_Normal)
                
            sizelabel = _('Archive Size') + " :"
            
            self.filechecklist.CheckItem(root, True)
        else:
            # Single file
            root = self.filechecklist.AddRoot(basepath)
            self.filechecklist.SetPyData(root, None)
            
            self.filechecklist.SetItemImage(root, self.images['file'], which = wx.TreeItemIcon_Normal)
            
            size = calcsize(basepath)
            self.files = [([], basepath, basepath, size, None)]
            self.filemap[0] = root

            sizelabel = _('File size') + " :"
        
        self.filechecklist.Expand(root)
        
        self.sizelabel.SetLabel(sizelabel)
        
        self._uncheckIgnoredFiles()
        
        self._calcTotalSize()
        
    def _onItemChecked(self, event = None):
        """
        Handle when an item is checked
        """
        self._calcTotalSize()
        
    def _uncheckIgnoredFiles(self):
        """
        By default, uncheck files that should be ignored
        """
        # Nothing to do for a single file
        if not self.isdir:
            return
        
        for index in range(len(self.filemap)):
            treeitem = self.filemap[index]
            filename = self.files[index][2]
            
            if shouldIgnore(filename):
                self.filechecklist.CheckItem(treeitem, False)
        
    def getCheckedFiles(self):
        """
        Get a list of which files are checked
        """
        checkedfiles = []
        
        if self.isdir:
            for index in range(len(self.filemap)):
                treeitem = self.filemap[index]
                if treeitem.IsChecked():
                    checkedfiles.append(self.files[index])
        else:
            if self.files:
                checkedfiles.append(self.files[0])
                
        return checkedfiles
        
    def _calcTotalSize(self):
        """
        Calculate the total size of the selected files
        """
        totalsize = 0L
        
        checkedfiles = self.getCheckedFiles()
        
        filecount = len(checkedfiles)
        
        for patharray, fullpath, filename, size, addto in checkedfiles:
            totalsize += size
        
        sizestring = _('%s (%s bytes)') % (utility.size_format(totalsize), comma_format(totalsize))
        
        self.numfileslabel.SetLabel(str(filecount))
        
        self.sizetext.SetLabel(sizestring)
        
    def _newFileSelection(self, newpath):
        self.basepath = newpath
        self._getFileListing(newpath)

    def _selectDir(self, event = None):
        dlg = wx.DirDialog(self.dialog, 
                           _('Select a directory'), 
                           style = wx.DD_DEFAULT_STYLE | wx.DD_NEW_DIR_BUTTON)
        if dlg.ShowModal() == wx.ID_OK:
            self._newFileSelection(dlg.GetPath())
        dlg.Destroy()

    def _selectFile(self, event = None):
        dlg = wx.FileDialog(self.dialog, 
                            _('Choose file or directory to use'), 
                            '', 
                            '', 
                            _('All Files') + ' (*.*)|*.*', 
                            wx.OPEN)
        if dlg.ShowModal() == wx.ID_OK:
            self._newFileSelection(dlg.GetPath())
        dlg.Destroy()

#    def selectdrop(self, x):
#        list = x.m_files
#        self.dirCtl.SetLabel(x[0])



################################################################

class TorrentMaker(wx.Frame):
    """
    Creates the dialog for making a torrent
    """
    def __init__(self, parent):
        self.parent = parent

        title = _('Create Torrent')
        wx.Frame.__init__(self, None, -1, title)

        try:
            self.SetIcon(utility.icon)
        except:
            pass

        panel = wx.Panel(self, -1)
        
        sizer = wx.BoxSizer(wx.VERTICAL)
        
        outerbox = wx.BoxSizer(wx.HORIZONTAL)

        self.notebook = wx.Notebook(panel, -1, style = wx.NB_NOPAGETHEME)
                
        self.fileInfoPanel = FileInfoPanel(self.notebook, self)
        self.notebook.AddPage(self.fileInfoPanel, _('File Info'))
        
        self.trackerInfoPanel = TrackerInfoPanel(self.notebook, self)
        self.notebook.AddPage(self.trackerInfoPanel, _('Tracker Info'))

        self.miscInfoPanel = MiscInfoPanel(self.notebook, self)
        self.notebook.AddPage(self.miscInfoPanel, _('Misc. Info'))
        
        outerbox.Add(self.notebook, 2, wx.EXPAND|wx.ALL, 5)
        
        self.fileSelectionPanel = FileSelectionPanel(panel, self)
        
        outerbox.Add(self.fileSelectionPanel, 3, wx.EXPAND|wx.ALL, 5)
        
        sizer.Add(outerbox, 1, wx.EXPAND|wx.ALL, 5)
        
        btnbox = wx.BoxSizer(wx.HORIZONTAL)
        b3 = wx.Button(panel, -1, _('Save as default config'))
        btnbox.Add(b3, 0, wx.EXPAND)

        b2 = wx.Button(panel, -1, _('Make Torrent'))
        btnbox.Add(b2, 0, wx.EXPAND|wx.LEFT|wx.RIGHT, 10)

        b4 = wx.Button(panel, -1, _('Close'))
        btnbox.Add(b4, 0, wx.EXPAND)
        
        sizer.Add(btnbox, 0, wx.ALIGN_CENTER|wx.ALL, 10)

        wx.EVT_BUTTON(panel, b2.GetId(), self.complete)
        wx.EVT_BUTTON(panel, b3.GetId(), self.saveConfig)
        wx.EVT_BUTTON(panel, b4.GetId(), self.closeWin)
        accelTable = wx.AcceleratorTable([(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, b4.GetId())])
        self.SetAcceleratorTable(accelTable)

        panel.SetSizerAndFit(sizer)
        
        self.Fit()
        
        self.Show()

    def closeWin(self, event = None):
        utility.actions[ACTION_MAKER].torrentmaker = None
        
        savetordeffolder = self.fileInfoPanel.savetordeftext.GetValue()
        utility.makerconfig.Write('savetordeffolder', savetordeffolder)
        utility.makerconfig.Write('announcehistory', self.trackerInfoPanel.announcehistory, "bencode-list")

        self.Destroy()
        
    def saveConfig(self, event = None):
        self.fileInfoPanel.saveConfig()
        self.trackerInfoPanel.saveConfig()
        self.miscInfoPanel.saveConfig()
        
        utility.makerconfig.Flush()
    
    def complete(self, event = None):
        checkedfiles = self.fileSelectionPanel.getCheckedFiles()
        files = [(patharray, fullpath) for patharray, fullpath, filename, size, addto in checkedfiles]
        
        basepath = self.fileSelectionPanel.basepath
        if not files or not basepath:
            dlg = wx.MessageDialog(self, message = _('You must select a file or directory'), 
                caption = _('Error'), style = wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()
            return
        
        params = self.fileInfoPanel.getParams()
        params = union(params, self.trackerInfoPanel.getParams())
        params = union(params, self.miscInfoPanel.getParams())

        try:
            CompleteDir(self, basepath, params['announce'], params, files)
        except:
            oldstdout = sys.stdout
            sys.stdout = sys.stderr
            print_exc()
            sys.stdout = oldstdout


################################################################

class CompleteDir(wx.EvtHandler, SafeInvocation):
    """
    Create torrents for one or more files
    """
    def __init__(self, parent, d, announce, params, filelist):
        wx.EvtHandler.__init__(self)
        SafeInvocation.__init__(self)
        self.doneflag = Event()

        self.d = d
        self.a = announce
        self.filelist = filelist
        self.params = params
        self.parent = parent
        self.separatetorrents = False
        self.generatedfiles = []
        
        # See if we need to get md5sums for each file
        if 'gethash' in params:
            self.gethash = params['gethash']
        else:
            self.gethash = None
            
        # Can remove it from params before we pass things on
        if 'gethash' in params:
            del params['gethash']

        if isdir(d):
            self.choicemade = Event()
            frame = wx.Frame(None, -1, _('Make Torrent'), size = (1, 1))
            self.frame = frame
            panel = wx.Panel(frame, -1)
            gridSizer = wx.FlexGridSizer(cols = 1, vgap = 8, hgap = 8)
            gridSizer.AddGrowableRow(1)
            gridSizer.Add(wx.StaticText(panel, -1, 
                           _('Do you want to make a separate .torrent\nfor every item in this directory?')),
                           0, wx.ALIGN_CENTER)
            gridSizer.Add(wx.StaticText(panel, -1, ''))

            b = wx.FlexGridSizer(cols = 3, hgap = 10)
            yesbut = wx.Button(panel, -1, _('Yes'))
            def saidyes(e, self = self):
                self.frame.Destroy()
                self.separatetorrents = True
                self.begin()
            wx.EVT_BUTTON(frame, yesbut.GetId(), saidyes)
            b.Add(yesbut, 0)

            nobut = wx.Button(panel, -1, _('No'))
            def saidno(e, self = self):
                self.frame.Destroy()
                self.begin()
            wx.EVT_BUTTON(frame, nobut.GetId(), saidno)
            b.Add(nobut, 0)

            cancelbut = wx.Button(panel, -1, _('Cancel'))
            def canceled(e, self = self):
                self.frame.Destroy()                
            wx.EVT_BUTTON(frame, cancelbut.GetId(), canceled)
            b.Add(cancelbut, 0)
            gridSizer.Add(b, 0, wx.ALIGN_CENTER)
            border = wx.BoxSizer(wx.HORIZONTAL)
            border.Add(gridSizer, 1, wx.EXPAND | wx.ALL, 4)
            
            panel.SetSizerAndFit(border)
            panel.SetAutoLayout(True)
            frame.Show()
            border.Fit(panel)
            frame.Fit()
        else:
            self.begin()

    def begin(self):
        if self.separatetorrents:
            frame = wx.Frame(None, -1, _('Make Directory'), size = wx.Size(550, 250))
        else:
            frame = wx.Frame(None, -1, _('Make Torrent'), size = wx.Size(550, 250))
        self.frame = frame

        panel = wx.Panel(frame, -1)
        gridSizer = wx.FlexGridSizer(cols = 1, vgap = 15, hgap = 8)

        if self.separatetorrents:
            self.currentLabel = wx.StaticText(panel, -1, _('Checking file sizes'))
        else:
            self.currentLabel = wx.StaticText(panel, -1, _('Building '))
        gridSizer.Add(self.currentLabel, 0, wx.EXPAND)
        self.gauge = wx.Gauge(panel, -1, range = 1000, style = wx.GA_SMOOTH)
        gridSizer.Add(self.gauge, 0, wx.EXPAND)
        gridSizer.Add((10, 10), 1, wx.EXPAND)
        self.button = wx.Button(panel, -1, _('Cancel'))
        gridSizer.Add(self.button, 0, wx.ALIGN_CENTER)
        gridSizer.AddGrowableRow(2)
        gridSizer.AddGrowableCol(0)

        g2 = wx.FlexGridSizer(cols = 1, vgap = 15, hgap = 8)
        g2.Add(gridSizer, 1, wx.EXPAND | wx.ALL, 25)
        g2.AddGrowableRow(0)
        g2.AddGrowableCol(0)
        panel.SetSizer(g2)
        panel.SetAutoLayout(True)
        wx.EVT_BUTTON(frame, self.button.GetId(), self.done)
        wx.EVT_CLOSE(frame, self.done)
        frame.Show(True)
        Thread(target = self.complete).start()

    def complete(self):        
        if self.parent.fileInfoPanel.startnow.GetValue():
            targeted = self.parent.fileInfoPanel.getTargeted()
            self.startnow = True
        else:
            self.startnow = False

        try:
            if self.separatetorrents:
                completedir(self.d,
                            self.a,
                            self.params,
                            self.doneflag,
                            self.valCallback,
                            self.fileCallback,
                            gethash = self.gethash,
                            filelist = self.filelist)
            else:
                make_meta_file(self.d,
                               self.a,
                               self.params,
                               self.doneflag,
                               self.valCallback,
                               progress_percent = 1,
                               fileCallback = self.fileCallback, 
                               gethash = self.gethash,
                               filelist = self.filelist)
            if not self.doneflag.isSet():
                self.completeCallback()

            if self.startnow:
                # When seeding immediately, copy torrents to config dir
                for list in self.generatedfiles:
                    torrentfile = list[1]
                    copy2(normpath(torrentfile), os.path.join(utility.getConfigPath(), "torrent"))
                    torrentfile4seed = os.path.join(utility.getConfigPath(), "torrent", split(normpath(torrentfile))[1])
                    list.append(torrentfile4seed)
        except (OSError, IOError), e:
		self.errorCallback(e)

    def errorCallback(self,e):
    	self.invokeLater(self.onError,[e])
	
    def onError(self,e):
            self.currentLabel.SetLabel(_('Error'))
            self.button.SetLabel(_('Close'))
            dlg = wx.MessageDialog(None, 
                                   message = _('Error') + ' - ' + str(e), 
                                   caption = _('Error'), 
                                   style = wx.OK | wx.ICON_ERROR)
            dlg.ShowModal()
            dlg.Destroy()

    def completeCallback(self):
    	self.invokeLater(self.onComplete)

    def onComplete(self):
        self.currentLabel.SetLabel(_('Done'))
        self.gauge.SetValue(1000)
        self.button.SetLabel(_('Close'))

    def valCallback(self, amount):
        self.invokeLater(self.onVal, [amount])

    def onVal(self, amount):
        target = int(amount * 1000)
        old = self.gauge.GetValue()
        perc5 = self.gauge.GetRange()/5
        if target > old + perc5: # 5% increments
            self.gauge.SetValue(target)

    def fileCallback(self, orig, torrent):
        self.generatedfiles.append([orig,torrent])
        self.invokeLater(self.onFile, [torrent])

    def onFile(self, torrent):
        self.currentLabel.SetLabel(_('Building') + torrent)

    def done(self, event):
        self.doneflag.set()
        self.frame.Destroy()
        if self.startnow:
            # When seeding immediately, add torrents to queue
            for list in self.generatedfiles:
                orig = list[0]
                torrentfile4seed = list[2]
                utility.queue.addtorrents.AddTorrentFromFile(torrentfile4seed, dest = orig)
